package edu.gettysburg.catapp1;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectOutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.app.DownloadManager;
import android.app.DownloadManager.Request;
import android.app.ListActivity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;

public class downloadtour extends ListActivity {

	//TODO: siteFeed in strings.xml once perfect
	private String siteFeed = "http://cs.gettysburg.edu/~lalvsa01/android/tours.xml";

	// XML node keys

	// list keys
	private static final String LIST_TOUR = "tour";
	private static final String LIST_ID = "id";
	private static final String LIST_NAME = "name";
	private static final String LIST_SIZE = "size";
	private static final String LIST_DESC = "desc";
	private static final String LIST_URL = "url";

	// tour keys
	private static final String TOUR_ID = "id";
	private static final String TOUR_NAME = "name";
	private static final String TOUR_SITE = "site";
	private static final String TOUR_MEDIA = "media";
	private static final String TOUR_LAT = "lat";
	private static final String TOUR_LNG = "lng";

	ArrayList<HashMap<String, String>> menuItems = new ArrayList<HashMap<String, String>>();

	public void onCreate(Bundle savedInstanceState) {

		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		new FetchAsyncTask().execute(siteFeed);

		// selecting single ListView item
		ListView lv = getListView();

		lv.setOnItemClickListener(new OnItemClickListener() {

			public void onItemClick(AdapterView<?> parent, View view, final int position, long id) {				
				// getting values from selected ListItem - keep for alerts
				String name = ((TextView) view.findViewById(R.id.name)).getText().toString();

				String toasted = name + " is being downloaded!!!";				
				Toast.makeText(getBaseContext(), toasted, Toast.LENGTH_LONG).show();

				Thread t = new Thread() {
					public void run() {
						downloadTour(position);	
					}

				};
				t.start();

			}
		});

	}

	private class FetchAsyncTask extends AsyncTask<String, Void, ArrayList<HashMap<String, String>>> {

		@Override
		// Actual download method, run in the task thread
		protected ArrayList<HashMap<String, String>> doInBackground(String... params) {
			// params comes from the execute() call: params[0] is the url.
			return getTours(params[0]);
		}

		@Override
		protected void onPostExecute(ArrayList<HashMap<String, String>> menuItems) {
			// Adding menuItems to ListView
			ListAdapter adapter = new SimpleAdapter(downloadtour.this, 
					menuItems, 
					R.layout.downloadtour_layout, 
					new String[] { LIST_NAME, LIST_DESC, LIST_SIZE }, 
					new int[] {	R.id.name, R.id.desc, R.id.size });
			setListAdapter(adapter);
		}
	}


	private ArrayList<HashMap<String, String>> getTours(String siteFeed) {
		try {
			URL url = new URL(siteFeed);

			HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection(); 
			int responseCode = httpConnection.getResponseCode();
			Log.v("response code", String.valueOf(responseCode));

			if (responseCode == HttpURLConnection.HTTP_OK) { 
				InputStream in = httpConnection.getInputStream(); 

				DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
				DocumentBuilder db = dbf.newDocumentBuilder();

				// parser
				Document doc = db.parse(in);      
				Element docEle = doc.getDocumentElement();

				NodeList tourList = docEle.getElementsByTagName(LIST_TOUR);
				if (tourList != null && tourList.getLength() > 0) {
					for (int i = 0 ; i < tourList.getLength(); i++) {
						HashMap<String, String> map = new HashMap<String, String>();
						Element entry = (Element)tourList.item(i);

						map.put(LIST_ID, entry.getElementsByTagName(LIST_ID).item(0).getFirstChild().getNodeValue());
						map.put(LIST_NAME, entry.getElementsByTagName(LIST_NAME).item(0).getFirstChild().getNodeValue());
						map.put(LIST_SIZE, entry.getElementsByTagName(LIST_SIZE).item(0).getFirstChild().getNodeValue() + "MB");
						map.put(LIST_DESC, entry.getElementsByTagName(LIST_DESC).item(0).getFirstChild().getNodeValue());
						map.put(LIST_URL, entry.getElementsByTagName(LIST_URL).item(0).getFirstChild().getNodeValue());

						// adding HashList to ArrayList
						menuItems.add(map);
					}
				}
				Log.v("connection", "si");				
			}
			else {
				Toast.makeText(getBaseContext(), "Please make sure you have connected to a cellular or WIFI network!", Toast.LENGTH_LONG).show();
				Log.v("connection", "no");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return menuItems;
	}

	private void downloadTour(final int n) {
		try {
			URL url = new URL(menuItems.get(n).get(LIST_URL));

			URLConnection connection = url.openConnection();

			HttpURLConnection httpConnection = (HttpURLConnection)connection; 
			int responseCode = httpConnection.getResponseCode(); 

			if (responseCode == HttpURLConnection.HTTP_OK) { 
				InputStream in = httpConnection.getInputStream(); 

				DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
				DocumentBuilder db = dbf.newDocumentBuilder();

				// parser
				Document doc = db.parse(in);      
				Element docEle = doc.getDocumentElement();

				DownloadManager dm = (DownloadManager)getSystemService(Context.DOWNLOAD_SERVICE);

				final ArrayList<Long> refList = new ArrayList<Long>();
				final ArrayList<String> nameList = new ArrayList<String>();

				new Thread() {
					public void run() {
						monitorDownloads(refList, nameList, n);
					}
				}.start();

				NodeList siteList = docEle.getElementsByTagName(TOUR_SITE);

				if (siteList != null && siteList.getLength() > 0) {

					// initialize arrays
					ArrayList<String> sites = new ArrayList<String>();
					ArrayList<Integer> sitePoints = new ArrayList<Integer>();
					ArrayList<Integer> allPoints = new ArrayList<Integer>();

					for (int i = 0 ; i < siteList.getLength(); i++) {
						Element entry = (Element)siteList.item(i);

						String id = entry.getElementsByTagName(TOUR_ID).item(0).getFirstChild().getNodeValue();
						String name = entry.getElementsByTagName(TOUR_NAME).item(0).getFirstChild().getNodeValue();
						String media = entry.getElementsByTagName(TOUR_MEDIA).item(0).getFirstChild().getNodeValue();

						NodeList latList = entry.getElementsByTagName(TOUR_LAT);
						NodeList lngList = entry.getElementsByTagName(TOUR_LNG);

						sites.add(name);

						for (int j = 0; j < latList.getLength(); j++) {
							String lat1 = latList.item(j).getFirstChild().getNodeValue();
							String lng1 = lngList.item(j).getFirstChild().getNodeValue();

							Double lat = Double.parseDouble(lat1) * 1e6;
							Double lng = Double.parseDouble(lng1) * 1e6;

							allPoints.add(lat.intValue());
							allPoints.add(lng.intValue());

							if (j == 0) {
								sitePoints.add(lat.intValue());
								sitePoints.add(lng.intValue());
							}
						}

						// start downloading
						String downloadName = TOUR_SITE + id;
						String completeDownloadName = downloadName + ".zip";
						DownloadManager.Request request = new Request(Uri.parse(media));
						request.setDestinationInExternalFilesDir(getBaseContext(), Environment.DIRECTORY_DOWNLOADS + "/" + menuItems.get(n).get(LIST_NAME), completeDownloadName);
						long refID = dm.enqueue(request);
						refList.add(refID);
						nameList.add(completeDownloadName);
						request.setTitle(downloadName);
						request.setDescription(name);
						request.setNotificationVisibility(Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
					}	

					// start encoding					
					final Tour tour = new Tour(menuItems.get(n).get(LIST_DESC), sites, sitePoints, allPoints);
					new Thread() {
						public void run() {
							encode(getApplicationContext().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS) + "/" + menuItems.get(n).get(LIST_NAME), tour);
						}
					}.start();
				}

				Log.v("connection2", "si");
			}
			else {
				Log.v("connection2", "no");
			}
		} catch (Exception e) {

		}
	}

	private void monitorDownloads(final ArrayList<Long> refList, final ArrayList<String> nameList, final int n) {

		IntentFilter filter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);

		BroadcastReceiver receiver = new BroadcastReceiver() {
			@Override
			public void onReceive(Context context, Intent intent) {
				final long reference = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
				if (refList.contains(reference)) {
					final String path = getBaseContext().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).getPath();
					new Thread() {
						{
							unpackZip(path + "/" + menuItems.get(n).get(LIST_NAME) + "/", nameList.get(refList.indexOf(reference)), nameList.get(refList.indexOf(reference)).replace(".zip", ""));
						}
					}.start();
					refList.set(refList.indexOf(reference), (long) 0);
				}
			}
		};

		registerReceiver(receiver, filter);
	}
	
	private boolean encode(String path, Tour t) {
		try {
			FileOutputStream fos = new FileOutputStream(path + "/tour.dat");
			ObjectOutputStream oos = new ObjectOutputStream(fos);
			oos.writeObject(t);
			oos.close();
			Log.v("tour.dat", "ok");
			return true;
		}
		catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}

	private boolean unpackZip(String path, String zipname, String newpath)
	{       
		InputStream is;
		ZipInputStream zis;
		try 
		{
			String filename;
			is = new FileInputStream(path + zipname);
			zis = new ZipInputStream(new BufferedInputStream(is));          
			ZipEntry ze;
			byte[] buffer = new byte[1024];
			int count;
			File fileLoc = new File(path + newpath);
			if(fileLoc.mkdir()) {
				//Toast.makeText(getBaseContext(), "Directory made.", Toast.LENGTH_SHORT).show();	
			}

			while ((ze = zis.getNextEntry()) != null) 
			{
				filename = ze.getName();
				FileOutputStream fout = new FileOutputStream(fileLoc.getPath()+"/" + filename);

				while ((count = zis.read(buffer)) != -1) 
				{
					fout.write(buffer, 0, count);             
				}

				fout.close();               
				zis.closeEntry();
			}

			zis.close();
			//Toast.makeText(getBaseContext(), path + zipname + "File unzipped.", Toast.LENGTH_SHORT).show();
			File file = new File(path + zipname);
			boolean deleted = file.delete();

			if(deleted == false) {
				Toast.makeText(getBaseContext(), path + zipname + "Not deleted.", Toast.LENGTH_SHORT).show();
			}
		} 
		catch(IOException e)
		{
			e.printStackTrace();
			return false;
		}

		return true;
	}

}